package security.channel;

import java.io.IOException;
import java.net.Socket;
import java.security.NoSuchAlgorithmException;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;

/**
 * Class to sent secure messages over a network. Either first a message is
 * encrypted then base64 encoded and then sent over a network. Or first a
 * message is received from the network then base64 decoded and then decrypted.
 * This class is used by any subclass with specific algorithms and methods to
 * encrypt or decrypt messages to be sent then over a network.
 */
public abstract class SecureChannel extends Base64Channel {

	protected Cipher encrypt = null;
	protected Cipher decrypt = null;

	public SecureChannel(Socket socket) throws IOException {
		super(socket);
	}

	/**
	 * Creates a new Cipher with the specified algorithm.
	 * 
	 * @param algorithm
	 *            the algorithm the cipher uses.
	 */
	protected void createCipher(String algorithm) {
		try {
			this.encrypt = Cipher.getInstance(algorithm);
			this.decrypt = Cipher.getInstance(algorithm);
		} catch (NoSuchAlgorithmException e) {
			System.err.println("ERROR: initialize ciphers in "
					+ this.getClass() + ", no such algorithm: "
					+ e.getMessage());
		} catch (NoSuchPaddingException e) {
			System.err.println("ERROR: initialize ciphers in "
					+ this.getClass() + ", no such padding: " + e.getMessage());
		}
	}

	/* (non-Javadoc)
	 * @see security.channel.Base64Channel#println(java.lang.String)
	 */
	@Override
	public void println(String message) {
		byte[] encrypted = null;
		//System.out.println("vor encrypting: " + message);

		try { // encrypt message
			encrypted = encrypt.doFinal(message.getBytes());
		} catch (IllegalBlockSizeException e) {
			System.err.println("ERROR: encrypt in " + this.getClass()
					+ ", illegal blocksize: " + e.getMessage());
		} catch (BadPaddingException e) {
			System.err.println("ERROR: encrypt in " + this.getClass()
					+ ", bad padding exception: " + e.getMessage());
		}
		//System.out.println("nach enrypt:" + encrypted); // TODO delete
		super.printlnBytes(encrypted);
	}

	/* (non-Javadoc)
	 * @see security.channel.Base64Channel#readLine()
	 */
	@Override
	public String readLine() throws IOException {
		byte[] base64Decoded = super.readLineBytes();

		if (base64Decoded == null) { // if socket receives null do not decrypt
			return null;
		}
		String notbase64Decoded = new String(base64Decoded);
		if (notbase64Decoded.equals("!list") || notbase64Decoded.equals("!end") || notbase64Decoded.startsWith("!notok")) {
			return notbase64Decoded;
		}

		//System.out.println("vor decrypting:" + base64Decoded); // TODO delete

		byte[] decrypted = null;
		try { // decrypt message
			decrypted = decrypt.doFinal(base64Decoded);
		} catch (IllegalBlockSizeException e) {
			System.err.println("ERROR: decrypt in " + this.getClass()
					+ ", illegal blocksize: " + e.getMessage());
		} catch (BadPaddingException e) {
			System.err.println("ERROR: decrypt in " + this.getClass()
					+ ", bad padding exception: " + e.getMessage());
		}
		String decryptedString = new String(decrypted);
		//System.out.println("nach decrypting:" + decryptedString); // TODO delete
		return decryptedString;
	}

}
